/**
 * 麻将逻辑
 */
class MjLogic {

    /**
     * 牌型
     */
    public static CardType = {
        CHR_QIANG_GANG:0x00000001,	//抢杠
        CHR_GANG_SHANG_PAO:0x00000002,	//杠上炮
        CHR_GANG_KAI:0x00000004,	//杠上花

        CHR_QING_YI_SE:0x00000010,	//清一色   4红中
        CHR_HUN_YI_SE:0x00000020,	//混一色   7小对
        CHR_DA_DIAO_CHE:0x00000040,	//大吊车
        CHR_MEI_BAI_DA:0x00000080,	//没百搭
        CHR_SAN_BAI_DA:0x00000100,	//三百搭
        CHR_SHUANG_SE_ZI:0x00000200,	//双色子
        CHR_DUI_DUI_HU:0x00000400,	//对对胡
        CHR_SHI_HUA:0x00000800,	//四花
        CHR_LAOTOU_BAIDA:0x00001000,	//老头百搭

        CHR_ZI_MO:0x00002000,	//自摸
        CHR_SI_HONG_ZHONG:0x00004000,	//四红中
        CHR_QI_XIAO_DUI:0x00008000,	//7小对
    }

    public static getCardType(cardData: number): string {
        var result:string="";
        switch (cardData)
        {
            case MjLogic.CardType.CHR_QING_YI_SE:
                result = "清一色";
                break;
            case MjLogic.CardType.CHR_HUN_YI_SE:
                 result = "混一色";
                break;
            case MjLogic.CardType.CHR_DA_DIAO_CHE:
                result = "大吊车";
                break;
            case MjLogic.CardType.CHR_MEI_BAI_DA:
                result = "没百搭";
                break;
            case MjLogic.CardType.CHR_SAN_BAI_DA:
                result = "三百搭";
                break;
            case MjLogic.CardType.CHR_DUI_DUI_HU:
                result = "对对胡";
                break;
            case MjLogic.CardType.CHR_SHI_HUA:
                result = "四花";
                break;
            case MjLogic.CardType.CHR_LAOTOU_BAIDA:
                result = "老头百搭";
                break;
            case MjLogic.CardType.CHR_SI_HONG_ZHONG:
                result = "四红中";
                break;
            case MjLogic.CardType.CHR_QI_XIAO_DUI:
                result = "7小对";
                break;
        }
        return result;
    }
     
    /**
     * 操作类型
     */
    public static Prio = {
        Pass: 0x00,
        ChiLeft: 0x01,
        ChiRight: 0x02,
        ChiMiddle: 0x04,
        Peng: 0x08,
        Gang: 0x10,
        Ting: 0x20,
        ChiHu: 0x40,
        BuHua: 0x60,
        ZiMo: 0x80,
    }
    /**
     * 花色掩码
     */
    public static MASK_VALUE: number = 0x0F;
    /**
     * 数值掩码
     */
    public static MASK_COLOR: number = 0xF0;
    /**
     * 麻将基础数据
     */
    public static cardDataArray: number[] = [
	    0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,//万子牌值：1-9
		0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,//万子
		0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,//万子
		0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,//万子
		0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,//索子牌值：17-25
		0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,//索子
		0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,//索子
		0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,//索子
		0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,//同子牌值：33-41
		0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,//同子
		0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,//同子
		0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,//同子

		0x31,0x32,0x33,0x34,0x35,0x36,0x37,	//东西南北中发白牌值：49~55
		0x31,0x32,0x33,0x34,0x35,0x36,0x37,	//东西南北中发白
		0x31,0x32,0x33,0x34,0x35,0x36,0x37,	//东西南北中发白
		0x31,0x32,0x33,0x34,0x35,0x36,0x37,	//东西南北中发白

//0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,	//花牌牌值：64-72
    ]
    /**
     * 获取数值
     * @param 牌数据
     * @returns 花色
     */
    public static getCardValue(cardData: number): number {
        return (cardData & this.MASK_VALUE);
    }
    /**
     * 获取花色
     * @param 牌数据
     * @returns 花色
     */
    public static getCardColor(cardData: number): number {
        return (cardData & this.MASK_COLOR) / 16;
    }
    /**
     * 检查是否可以吃牌
     * @param cardData 手牌数据
     * @param putCard 出的牌
     * @returns 吃牌类型列表
     */
    public static checkChi(cardData: number[], putCard: number): number[] {
        var result = [];
        if (cardData.indexOf(putCard - 1) != -1 && cardData.indexOf(putCard - 2) != -1) {
            result.push(this.Prio.ChiRight);
        }
        if (cardData.indexOf(putCard - 1) != -1 && cardData.indexOf(putCard + 1) != -1) {
            result.push(this.Prio.ChiMiddle);
        }
        if (cardData.indexOf(putCard + 1) != -1 && cardData.indexOf(putCard + 2) != -1) {
            result.push(this.Prio.ChiLeft);
        }
        return result;
    }
    /**
     * 检查是否可以碰牌
     * @param cardData 手牌数据
     * @param putCard 出的牌
     */
    public static checkPeng(cardData: number[], putCard: number): boolean {
        var oneIndex = cardData.indexOf(putCard);
        if (oneIndex != -1) {
            if (cardData.indexOf(putCard, oneIndex + 1) != -1) {
                return true;
            }
        }
        return false;
    }
    /**
     * 检查是否可以杠牌
     * @param cardData 手牌数据
     * @param putCard 出的牌
     */
    public static checkGang(cardData: number[], putCard: number): boolean {
        var oneIndex = cardData.indexOf(putCard);
        if (oneIndex != -1) {
            var twoIndex = cardData.indexOf(putCard, oneIndex + 1);
            if (twoIndex != -1) {
                if (cardData.indexOf(putCard, twoIndex + 1) != -1) {
                    return true;
                }
            }
        }
        return false;
    }
    /**
    * 检查是否可以暗杠牌
    * @param cardData 手牌数据
    */
    public static checkAnGang(cardData: number[]): number[] {
        var result = [];
        for (var i = 0; i < cardData.length; i++) {
            var card = cardData[i];
            if (this._checkAnGang(cardData, card) && result.indexOf(card) == -1) {
                result.push(card);
            }
        }
        return result;
    }
    /**
 * 检查是否可以东南西北旋风杠牌
 * @param cardData 手牌数据
 * @param putCard 出的牌
 */
    public static checkXuanFengGang4(cardData: number[]): boolean {
        var haveDong, haveNan, haveXi, haveBei = false;
        for (var i = 0; i < cardData.length; i++) {
            if (cardData[i] == 0x31) {
                haveDong = true;
            }
            if (cardData[i] == 0x41) {
                haveNan = true;
            }
            if (cardData[i] == 0x51) {
                haveXi = true;
            }
            if (cardData[i] == 0x61) {
                haveBei = true;
            }
        }
        return haveDong && haveNan && haveXi && haveBei;
    }
    /**
     * 检查是否可以中发白旋风杠牌
     * @param cardData 手牌数据
     * @param putCard 出的牌
     */
    public static checkXuanFengGang3(cardData: number[]): boolean {
        var haveZhong, haveFa, haveBai = false;
        var haveLaizi = false;
        for (let i = 0; i < cardData.length; i++) {
            if (cardData[i] == 0x71) {
                haveZhong = true;
            }
            if (cardData[i] == 0x81) {
                haveFa = true;
            }
            if (cardData[i] == 0x91) {
                haveBai = true;
            }
            if (cardData[i] == 0x99) {
                haveLaizi = true;
            }
        }
        if (haveZhong && haveFa && haveLaizi) {
            return true;
        }
        if (haveZhong && haveBai && haveLaizi) {
            return true;
        }
        if (haveBai && haveFa && haveLaizi) {
            return true;
        }
        return haveZhong && haveFa && haveBai;
    }
    public static _checkAnGang(cardData: number[], card: number): boolean {
        var oneIndex = cardData.indexOf(card);
        if (oneIndex != -1) {
            var twoIndex = cardData.indexOf(card, oneIndex + 1);
            if (twoIndex != -1) {
                if (cardData.indexOf(card, twoIndex + 1) != -1) {
                    var threeIndex = cardData.indexOf(card, twoIndex + 1);
                    if (cardData.indexOf(card, threeIndex + 1) != -1) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    public static checkHaveYaoJiu(card: Array<number>, pengList: Array<PengItem>): boolean {
        var result = false;
        var list = [];
        for (var k = 0; k < pengList.length; k++) {
            list = list.concat(pengList[k].data);
        }
        for (var i = 0; i < card.length; i++) {
            var cardValue = this.getCardValue(card[i]);
            var cardColor = this.getCardColor(card[i]);
            if ((cardValue == 0x9 && cardColor < 0x3) || cardValue == 0x1 || (cardColor >= 3 && cardValue == 1)) {//如果有1、9或者风
                return true;
            }
        }
        for (var j = 0; j < list.length; j++) {
            var cardValue = this.getCardValue(list[j]);
            if ((cardValue == 0x9 && cardColor < 0x3) || cardValue == 0x1 || (cardColor >= 3 && cardValue == 1)) {//如果有1、9或者风
                return true;
            }
        }
        return false;
    }

    /**
      * 检测自摸
      * @param cardData 手牌数据
      * @param twoNum 将牌数量，可以不传
      * @param threeNum 刻子和顺子数量，可以不传
      * @returns 可以胡牌的牌型排列组合
      */
    public static checkHu(cardData: number[], pengList: Array<PengItem>, twoNum: number = -1, threeNum: number = -1, putCard: number = 0): number[][] {
        var result = null;
        if (twoNum == -1) { twoNum = 1; }
        if (threeNum == -1) { threeNum = (cardData.length - 2) / 3; }
        result = this._checkHu(cardData, 7, 0, null, pengList);
        if (result) {
            return result;
        }
        if (this.checkHaveYaoJiu(cardData, pengList) && this.checkQueMen(cardData, pengList)) {
            result = this._checkHu(cardData, twoNum, threeNum, null, pengList);
        }
        return result;
    }
    /**
     * 检查听牌
     * @param cardData 手牌数据
     * @param operateList 吃碰杠数据
     * @returns 可以打出即听牌的牌列表
     */
    public static checkTing(cardData: number[], pengList: Array<PengItem>): number[] {
        var result = [];
        if (pengList.length > 0) {
            return result;
        }
        var checkedList = [];
        for (var i = 0; i < cardData.length; i++) {
            if (checkedList.indexOf(cardData[i]) == -1) {//防止重复判断
                var tempCardData = cardData.concat();
                tempCardData[i] = 0x99;//替换牌
                checkedList.push(cardData[i]);
                if (this.checkHu(tempCardData, pengList) != null) {
                    result.push(cardData[i]);
                }
            }
        }
        return result;
    }
    /**
     * 检测点炮
     * @param cardData 手牌数据
     * @param putCard 出牌数据
     * @param twoNum 将牌数量，可以不传
     * @param threeNum 刻子和顺子数量，可以不传
     * @returns 可以胡牌的牌型排列组合
     */
    public static checkHuByPutCard(cardData: number[], putCard: number, pengList: Array<PengItem>, twoNum: number = -1, threeNum: number = -1): number[][] {
        var newCardData = cardData.concat();
        newCardData.push(putCard);
        if (twoNum == -1) { twoNum = 1; }
        if (threeNum == -1) { threeNum = (newCardData.length - 2) / 3; }
        return this.checkHu(newCardData, pengList, twoNum, threeNum);
    }
    /**
     * 检查是否有三张一样的
     * @param cardData 手牌数据
     * @param operateList 吃碰杠列表
     */
    private static checkHaveTriple(huList: number[][], pengList: PengItem[]): boolean {
        for (let i = 0; i < pengList.length; i++) {
            if (pengList[i].type != MjLogic.Prio.ChiLeft &&
                pengList[i].type != MjLogic.Prio.ChiMiddle &&
                pengList[i].type != MjLogic.Prio.ChiRight) {
                return true;
            }
        }
        var twoNum = 0;
        for (var i = 0; i < huList.length; i++) {
            var card0 = huList[i][0];
            var card1 = huList[i][1];
            if (huList[i].length == 3) {
                var card2 = huList[i][2];
                if ((card0 == card1 && card1 == card2) ||
                    (card0 == 0x99 && card1 == card2) ||
                    (card1 == 0x99 && card1 == card2) ||
                    (card0 == card1 && card2 == 0x99)
                ) {
                    return true;
                }
                if (this.checkXuanFengGang3(huList[i])) {//如果是三连或者中发白
                    return true;
                }
            }
            else {
                twoNum++;
                if (MjLogic.getCardColor(card0) > 0x6 && MjLogic.getCardColor(card1) > 0x6) {//如果将牌是中发白任意一个
                    return true;
                }
                if ((card0 != 0x99 && MjLogic.getCardColor(card0)) > 0x6 || (card1 != 0x99 && MjLogic.getCardColor(card1) > 0x6)) {//如果将牌是中发白任意一个
                    return true;
                }
            }
        }
        if (twoNum == 7) {
            return true;
        }
        return false;
    }
    private static _checkHu(cardData: number[], twoNum: number, threeNum: number, saveMajing: number[][], pengList: PengItem[]): number[][] {
        if (cardData == null) {
            if (twoNum === 0 && threeNum === 0) {
                if (!this.checkHaveTriple(saveMajing, pengList)) {
                    return null;
                }
                return saveMajing;
            }
            else {
                return null;
            }
        }
        if (cardData.length === ((twoNum * 2) + (threeNum * 3))) {
            if (threeNum > 0) {
                var indexs = this.siphonThreeIndexs(cardData.length);
                for (var i = 0; i < indexs.length; i++) {
                    var index = indexs[i];
                    var card0 = cardData[index[0]];
                    var card1 = cardData[index[1]];
                    var card2 = cardData[index[2]];
                    var cards = [card0, card1, card2];
                    cards.sort();
                    if (this.isCanThree(cards[0], cards[1], cards[2])) {
                        var saveMajiangesCache = this.appendSomeMajiang(saveMajing, [cardData[index[0]], cardData[index[1]], cardData[index[2]]]);
                        var majiangesReturn = this._checkHu(this.removeSomeMajiang(cardData, [index[0], index[1], index[2]]), twoNum, threeNum - 1, saveMajiangesCache, pengList);
                        if (majiangesReturn != null) {
                            return majiangesReturn;
                        }
                    }
                }
            }
            else if (twoNum > 0) {
                var indexs1 = this.siphonTwoIndexs(cardData.length);
                if (indexs1 == null) {
                    return null;
                }
                for (var i1 = 0; i1 < indexs1.length; i1++) {
                    var index1 = indexs1[i1];
                    var card01 = cardData[index1[0]];
                    var card11 = cardData[index1[1]];
                    if (this.isCanTwo(card01, card11)) {
                        var saveMajiangesCache1 = this.appendSomeMajiang(saveMajing, [cardData[index1[0]], cardData[index1[1]]]);
                        var mahjongTilesesReturn = this._checkHu(this.removeSomeMajiang(cardData, [index1[0], index1[1]]), twoNum - 1, threeNum, saveMajiangesCache1, pengList);
                        if (mahjongTilesesReturn != null) {
                            return mahjongTilesesReturn;
                        }
                    }
                }
            }
            else {
                return saveMajing;
            }
        }
        return null;
    }

    /**
     * 检查缺门
     */
    private static checkQueMen(card: Array<number>, pengList: Array<PengItem>): boolean {
        var tiaoList = [];//条个数
        var tongList = [];//筒个数
        var wanList = [];//万个数
        var hunList = [];//风个数
        var list = [];
        for (var k = 0; k < pengList.length; k++) {
            list = list.concat(pengList[k].data);
        }
        var cards = card.concat(list);
        var haveLaizi = false;
        for (var i = 0; i < cards.length; i++) {
            haveLaizi = cards[i] == 0x99;
            var cardColor = this.getCardColor(cards[i]);
            if (cardColor == 0) {
                tiaoList.push(cards[i]);
            }
            else if (cardColor == 1) {
                tongList.push(cards[i]);
            }
            else if (cardColor == 2) {
                wanList.push(cards[i]);
            }
            else {
                hunList.push(cards[i]);
            }
        }
        if ((tiaoList.length == 0 && tongList.length == 0 && wanList.length > 0) ||
            (tiaoList.length == 0 && wanList.length == 0 && tongList.length > 0) ||
            (wanList.length == 0 && tongList.length == 0 && tiaoList.length > 0)) {
            if (hunList.length == 0) {//清一色
                return true;
            }
        }
        if (tiaoList.length == 0 || tongList.length == 0 || wanList.length == 0) {
            return false;
        }
        return true;
    }
    public static isCanThree(card0: number, card1: number, card2: number): boolean {
        //let gameConfig = RES.getRes("gameconfig_json");
        //if (gameConfig.isopen) {
        if (1==1) {
            var array = [card0, card1, card2];
            array.sort();
            if (array[0] == 0x71 && array[1] == 0x81 && array[2] == 0x91) {
                return true;
            }
            if (array[0] == 0x81 && array[1] == 0x91 && array[2] == 0x99) {
                return true;
            }
            if (array[0] == 0x71 && array[1] == 0x91 && array[2] == 0x99) {
                return true;
            }
            if (array[0] == 0x71 && array[1] == 0x81 && array[2] == 0x99) {
                return true;
            }
        }
        if (card0 === card1 && card1 === card2) {
            return true;
        }
        if (Math.abs(((card0 - card1) | 0)) === 1 && Math.abs(((card1 - card2) | 0)) === 1) {
            return true;
        }
        if (card0 == 0x99 && Math.abs(card1 - card2) <= 2) {//通用赖子计算
            return true;
        }
        if (card1 == 0x99 && Math.abs(card0 - card2) <= 2) {//通用赖子计算
            return true;
        }
        if (card2 == 0x99 && Math.abs(card0 - card1) <= 2) {//通用赖子计算
            return true;
        }
        return false;
    }
    public static isCanTwo(card0: number, card1: number): boolean {
        if (card0 === card1) {
            return true;
        }
        if (card0 == 0x99 || card1 == 0x99) {
            return true;
        }
        return false;
    }
    public static appendSomeMajiang(saveMajing: number[][], majiang: number[]) {
        if (saveMajing == null) {
            var majiangesReturn = [];
            majiangesReturn[0] = majiang;
            return majiangesReturn;
        }
        else {
            var majiangesReturn1 = [];
            majiangesReturn1 = saveMajing.concat();
            majiangesReturn1[saveMajing.length] = majiang;
            return majiangesReturn1;
        }
    }
    public static removeSomeMajiang(majiangs: number[], indexs: number[]): number[] {
        var lenNew = (majiangs.length - indexs.length) | 0;
        if (lenNew > 0) {
            var majiangNew = [];
            var index = 0;
            for (var i = 0; i < majiangs.length; i++) {
                var isAppend = true;
                for (var j = 0; j < indexs.length; j++) {
                    if (i === indexs[j]) {
                        isAppend = false;
                        break;
                    }
                }
                if (isAppend) {
                    majiangNew[index] = majiangs[i];
                    index++;
                }
            }
            return majiangNew;
        }
        return null;
    }
    public static twoIndexsCache: any = {};
    public static siphonTwoIndexs(arrayLen: number): number[][] {
        if (this.twoIndexsCache[arrayLen]) {
            return this.twoIndexsCache[arrayLen];
        }
        var len = (arrayLen * (arrayLen - 1)) / 2;
        if (len > 0) {
            var indexs = [];
            var index = 0;
            for (var i = 0; i < arrayLen; i = (i + 1) | 0) {
                for (var j = (((i + 1) | 0)); j < arrayLen; j = (j + 1) | 0) {
                    indexs[index] = [];
                    indexs[index][0] = i;
                    indexs[index][1] = j;
                    index = (index + 1) | 0;
                }
            }
            this.twoIndexsCache[arrayLen] = indexs;
            return indexs;
        }
        else {
            return null;
        }
    }
    public static threeIndexsCache: any = {};
    public static siphonThreeIndexs(arrayLen: number): number[][] {
        if (this.threeIndexsCache[arrayLen]) {
            return this.threeIndexsCache[arrayLen];
        }
        var len = (arrayLen * (arrayLen - 1) * (arrayLen - 2)) / 6;
        if (len > 0) {
            var indexs = [];
            var index = 0;
            for (var i = 0; i < arrayLen; i = (i + 1) | 0) {
                for (var j = (((i + 1) | 0)); j < arrayLen; j = (j + 1) | 0) {
                    for (var k = (((j + 1) | 0)); k < arrayLen; k = (k + 1) | 0) {
                        indexs[index] = [];
                        indexs[index][0] = i;
                        indexs[index][1] = j;
                        indexs[index][2] = k;
                        index = (index + 1) | 0;
                    }
                }
            }
            this.threeIndexsCache[arrayLen] = indexs;
            return indexs;
        }
        else {
            return null;
        }
    }
    //座位号转换
    public static getSeatID(chairID: number, myCid: number, totle: number = 4): number {
        return (chairID + totle - myCid) % totle;
    }
}
